﻿using System.Collections.Generic;
using System.Drawing;

namespace ColladaDotNet.Pipeline.MD3
{
	public class MD3File
	{
		public string Magic;// 	Magic number. As a string of 4 octets, reads "IDP3"; as unsigned little-endian 860898377 (0x33504449); as unsigned big-endian 1229213747 (0x49445033).

		public int VERSION;// 	MD3 version number, latest known is 15, but use the constant MD3_VERSION.

		public string NAME;// 	MD3 name, usually its pathname in the PK3. ASCII character string, NUL-terminated (C-style).

		public int FLAGS;// 	 ???
		public int NUM_FRAMES;// 	Number of Frame objects, with a maximum of MD3_MAX_FRAMES. Current value of MD3_MAX_FRAMES is 1024.
		public int NUM_TAGS;// 	Number of Tag objects, with a maximum of MD3_MAX_TAGS. Current value of MD3_MAX_TAGS is 16. There is one set of tags per frame so the total number of tags to read is (NUM_TAGS * NUM_FRAMES).
		public int NUM_SURFACES;// 	Number of Surface objects, with a maximum of MD3_MAX_SURFACES. Current value of MD3_MAX_SURFACES is 32.
		public int NUM_SKINS;// 	Number of Skin objects. I should note that I have not seen an MD3 using this particular field for anything; this appears to be an artifact from the Quake 2 MD2 format. Surface objects have their own Shader field.
		public int OFS_FRAMES;// 	Relative offset from start of MD3 object where Frame objects start. The Frame objects are written sequentially, that is, when you read one Frame object, you do not need to seek() for the next object.
		public int OFS_TAGS;// 	Relative offset from start of MD3 where Tag objects start. Similarly written sequentially.
		public int OFS_SURFACES;// 	Relative offset from start of MD3 where Surface objects start. Again, written sequentially.
		public int OFS_EOF;// 	Relative offset from start of MD3 to the end of the MD3 object. Note there is no offset for Skin objects.

		public Frame[] Frames;

		public Surface[] Surfaces;


		public COLLADA GetColladaFile()
		{
			var doc = COLLADA.CreateNewScene("md3Scene", "md3Physics");
			var scene = doc.GetElementById("md3Scene") as visual_scene;

			List<material> materials = new List<material>();
			int i = 0;
			foreach (var surface in Surfaces)
			{
				int j = i;
				foreach (var shader in surface.Shaders)
				{
					var eff = doc.CreateEffect("shader" + i + "-effect", Color.FromArgb(255, 128, 128, 128));
					materials.Add(doc.CreateMaterial("shader" + i + "-material", "shader" + i + "-material", eff));
					++i;
				}
				i = j;

				List<double> pos = new List<double>();
				List<double> nor = new List<double>();
				List<double> tex = new List<double>();

				foreach (var vert in surface.Verts[0])
				{
					pos.Add(vert.X);
					pos.Add(vert.Y);
					pos.Add(vert.Z);
					nor.Add(vert.nX);
					nor.Add(vert.nY);
					nor.Add(vert.nZ);
				}
				foreach (var vert in surface.TexCoords)
				{
					tex.Add(vert.ST[0]);
					tex.Add(1-vert.ST[1]);
					tex.Add(0);
				}
					if (pos.Count == 0)
					continue;

				var baseName = "model" + i;
				var node = scene.CreateNode(baseName + "-node", baseName);
				instance_controller instController = new instance_controller();

				//instance_geometry instGeom = new instance_geometry() { };
				//node.instance_geometry = new instance_geometry[] { instController };
				node.instance_controller = new instance_controller[] { instController };

				var geom = doc.CreateGeometry(baseName + "-mesh", baseName);

				var ctrl = doc.CreateController(baseName + "-morph");
				instController.url = "#"+ctrl.id;
				var morph1 = ctrl.CreateMorph(geom, MorphMethodType.RELATIVE);
				ctrl.Item = morph1;

				List<string> frameMeshName = new List<string>();

				for (int i1 = 0; i1 < Frames.Length; i1++)
				{
					var frame = Frames[i1];
					frameMeshName.Add(baseName + "-frame-" + i1 + "-" + frame.NAME.Replace(' ', '-').Replace('(', '-').Replace(')', '-'));
					
				}
				morph1.CreateTargetInput(morph1.CreateTargetsSource(ctrl.id + "-targets", frameMeshName.ToArray()));
				morph1.CreateWeightInput(morph1.CreateZeroWeights(ctrl.id + "-weights",frameMeshName.Count));
				

				//instGeom.url = "#" + geom.id;
				var mesh = geom.CreateMesh();

				var positionsSource = mesh.CreatePositionVertices(geom.id + "-vertices", pos.ToArray());
				var normals = mesh.CreateXYZSource(geom.id + "-normals", nor.ToArray());
				var map1 = mesh.CreateSTPSource(geom.id + "-map-channel-1", tex.ToArray());

				instController.bind_material = new bind_material() { };
				instController.bind_material.technique_common = new instance_material[surface.Shaders.Length];

				for (j = 0; j < surface.Shaders.Length; ++j)
					instController.bind_material.technique_common[j] = new instance_material() { symbol = "texture" + (i + j), target = "#" + materials[(i + j)].id };


				var triangles = mesh.CreateTriangles("texture" + (i+0));

				triangles.CreateVertexInput(positionsSource, 0);
				triangles.CreateNormalInput(normals, 1);
				triangles.CreateTexCoordInput(map1, 2, 1);

				int stride = 3;
				List<int> idx = new List<int>();
				foreach (var triangle in surface.Triangles)
				{
					for (j = 0; j < stride; ++j)
					{
						idx.Add(triangle.INDEXES[2]);
					}
					for (j = 0; j < stride; ++j)
					{
						idx.Add(triangle.INDEXES[1]);
					}
					for (j = 0; j < stride; ++j)
					{
						idx.Add(triangle.INDEXES[0]);
					}
				}
				triangles.CreateIndexArray(idx.ToArray());
				triangles.count = (ulong)surface.Triangles.Length;
				int f = 0;
				foreach(var frameName in frameMeshName)
				{
					var frameGeom = doc.CreateGeometry(frameName, frameName);
					var frameMesh = frameGeom.CreateMesh();

					pos.Clear();
					nor.Clear();
					foreach (var vert in surface.Verts[f])
					{
						pos.Add(vert.X);
						pos.Add(vert.Y);
						pos.Add(vert.Z);
						nor.Add(vert.nX);
						nor.Add(vert.nY);
						nor.Add(vert.nZ);
					}

					var fpositionsSource = frameMesh.CreatePositionVertices(frameGeom.id + "-vertices", pos.ToArray());
					var fnormals = frameMesh.CreateXYZSource(frameGeom.id + "-normals", nor.ToArray());
					var fmap1 = frameMesh.CreateSTPSource(frameGeom.id + "-map-channel-1", tex.ToArray());

					var ftriangles = frameMesh.CreateTriangles("texture" + (i + 0));

					ftriangles.CreateVertexInput(fpositionsSource, 0);
					ftriangles.CreateNormalInput(fnormals, 1);
					ftriangles.CreateTexCoordInput(fmap1, 2, 1);

					int fstride = 3;
					List<int> fidx = new List<int>();
					foreach (var triangle in surface.Triangles)
					{
						for (j = 0; j < fstride; ++j)
						{
							fidx.Add(triangle.INDEXES[2]);
						}
						for (j = 0; j < fstride; ++j)
						{
							fidx.Add(triangle.INDEXES[1]);
						}
						for (j = 0; j < fstride; ++j)
						{
							fidx.Add(triangle.INDEXES[0]);
						}
					}
					ftriangles.CreateIndexArray(fidx.ToArray());
					ftriangles.count = (ulong)surface.Triangles.Length;
					++f;
				}

				i += surface.Shaders.Length;
			}

			return doc;
		}
	}

}
